/*!
 * \file MatchEngine.cpp
 * \project WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 撮合引擎实现文件
 * 
 * \details 本文件实现了MatchEngine类的各种方法，用于在WonderTrader回测框架中模拟真实的交易撮合过程。
 *          主要实现功能包括：
 *          - 限价单撮合逻辑
 *          - 订单队列管理
 *          - 成交价格计算
 *          - 订单排队机制模拟
 *          - 撤单处理
 *          - 订单簿(LOB)维护
 */
#include "MatchEngine.h"
#include "../Includes/WTSDataDef.hpp"
#include "../Includes/WTSVariant.hpp"

#include "../Share/TimeUtils.hpp"
#include "../Share/decimal.h"
#include "../WTSTools/WTSLogger.h"

#define PRICE_DOUBLE_TO_INT_P(x) ((int32_t)((x)*10000.0 + 0.5))
#define PRICE_DOUBLE_TO_INT_N(x) ((int32_t)((x)*10000.0 - 0.5))
#define PRICE_DOUBLE_TO_INT(x) (((x)==DBL_MAX)?0:((x)>0?PRICE_DOUBLE_TO_INT_P(x):PRICE_DOUBLE_TO_INT_N(x)))

extern uint32_t makeLocalOrderID();

/*!
 * \brief 初始化撮合引擎
 * 
 * \param cfg 配置参数
 * 
 * \details 根据配置参数初始化撮合引擎。主要从配置中读取撤单比例参数，
 *          用于模拟真实交易中的撤单概率。如果配置为空则直接返回。
 */
void MatchEngine::init(WTSVariant* cfg)
{
	if (cfg == NULL)
		return;

	_cancelrate = cfg->getDouble("cancelrate");
}

/*!
 * \brief 清理撮合引擎
 * 
 * \details 清理撮合引擎中的所有未完成订单。通常在交易时段结束时调用，
 *          用于清理当日的所有挂单数据。
 */
void MatchEngine::clear()
{
	_orders.clear();
}

/*!
 * \brief 触发订单处理
 * 
 * \param stdCode 标准合约代码
 * \param to_erase 需要删除的订单ID列表
 * 
 * \details 处理新提交的订单，将状态为0（需要触发）的订单激活。
 *          主要完成以下工作：
 *          1. 遍历所有订单，查找待触发订单
 *          2. 向订单回调接口发送委托确认
 *          3. 向订单回调接口发送订单状态更新
 *          4. 将订单状态更新为1（已激活）
 */
void MatchEngine::fire_orders(const char* stdCode, OrderIDs& to_erase)
{
	for (auto& v : _orders)
	{
		uint32_t localid = v.first;
		OrderInfo& ordInfo = (OrderInfo&)v.second;

		if (ordInfo._state == 0)	// 需要触发
		{
			_sink->handle_entrust(localid, stdCode, true, "", ordInfo._time);
			_sink->handle_order(localid, stdCode, ordInfo._buy, ordInfo._left, ordInfo._limit, false, ordInfo._time);
			ordInfo._state = 1;
		}
	}
}

/*!
 * \brief 撮合订单处理
 * 
 * \param curTick 当前Tick数据
 * \param to_erase 需要删除的订单ID列表
 * 
 * \details 根据当前Tick数据撮合所有挂单。这是撮合引擎的核心功能，模拟真实交易所的撮合逻辑。
 *          主要处理流程：
 *          1. 处理待撤销订单（状态为9）
 *          2. 处理买单撮合逻辑
 *          3. 处理卖单撮合逻辑
 *          4. 考虑订单排队机制
 *          5. 计算成交价格和数量
 *          
 *          撮合规则：
 *          - 买单：当市场价格小于等于限价时成交
 *          - 卖单：当市场价格大于等于限价时成交
 *          - 支持排队机制模拟真实市场深度
 *          - 支持主动成交和被动成交两种模式
 */
void MatchEngine::match_orders(WTSTickData* curTick, OrderIDs& to_erase)
{
	uint64_t curTime = (uint64_t)curTick->actiondate() * 1000000000 + curTick->actiontime();
	uint64_t curUnixTime = TimeUtils::makeTime(curTick->actiondate(), curTick->actiontime());

	for (auto& v : _orders)
	{
		uint32_t localid = v.first;
		OrderInfo& ordInfo = (OrderInfo&)v.second;

		// 处理要撤单的订单
		if (ordInfo._state == 9)
		{
			_sink->handle_order(localid, ordInfo._code, ordInfo._buy, 0, ordInfo._limit, true, ordInfo._time);
			ordInfo._state = 99;

			to_erase.emplace_back(localid);

			WTSLogger::info_f("订单{}已撤销, 剩余数量: {}", localid, ordInfo._left*(ordInfo._buy ? 1 : -1));
			ordInfo._left = 0;
			continue;
		}

		if (ordInfo._state != 1 || curTick->volume() == 0)
			continue;

		// 处理买单撮合逻辑
		if (ordInfo._buy)
		{
			double price;
			double volume;

			// 根据主动成交标志决定使用哪个价格
			if (ordInfo._positive)
			{
				// 主动成交：使用卖一价格和数量
				price = curTick->askprice(0);
				volume = curTick->askqty(0);
			}
			else
			{
				// 被动成交：使用最新价格和成交量
				price = curTick->price();
				volume = curTick->volume();
			}

			// 买单成交条件：市场价格小于等于限价
			if (decimal::le(price, ordInfo._limit))
			{
				// 如果价格相等，需要考虑排队位置，价格不等说明已经全部轮到大单吃掉了
				if (!ordInfo._positive && decimal::eq(price, ordInfo._limit))
				{
					double& quepos = ordInfo._queue;

					// 如果成交量小于排队位置，不能成交
					if (volume <= quepos)
					{
						quepos -= volume;
						continue;
					}
					else if (quepos != 0)
					{
						// 如果成交量大于排队位置，部分可成交
						volume -= quepos;
						quepos = 0;
					}
				}
				else if (!ordInfo._positive)
				{
					// 被动成交时使用剩余订单量
					volume = ordInfo._left;
				}

				// 计算实际成交数量
				double qty = min(volume, ordInfo._left);
				if (decimal::eq(qty, 0.0))
					qty = 1;

				// 发送成交回调
				_sink->handle_trade(localid, ordInfo._code, ordInfo._buy, qty, ordInfo._price, price, ordInfo._time);

				// 更新订单状态
				ordInfo._traded += qty;
				ordInfo._left -= qty;

				// 发送订单状态更新
				_sink->handle_order(localid, ordInfo._code, ordInfo._buy, ordInfo._left, price, false, ordInfo._time);

				// 如果订单完全成交，加入删除列表
				if (ordInfo._left == 0)
					to_erase.emplace_back(localid);
			}
		}

		// 处理卖单撮合逻辑
		if (!ordInfo._buy)
		{
			double price;
			double volume;

			// 根据主动成交标志决定使用哪个价格
			if (ordInfo._positive)
			{
				// 主动成交：使用买一价格和数量
				price = curTick->bidprice(0);
				volume = curTick->bidqty(0);
			}
			else
			{
				// 被动成交：使用最新价格和成交量
				price = curTick->price();
				volume = curTick->volume();
			}

			// 卖单成交条件：市场价格大于等于限价
			if (decimal::ge(price, ordInfo._limit))
			{
				// 如果价格相等，需要考虑排队位置，价格不等说明已经全部轮到大单吃掉了
				if (!ordInfo._positive && decimal::eq(price, ordInfo._limit))
				{
					double& quepos = ordInfo._queue;

					// 如果成交量小于排队位置，不能成交
					if (volume <= quepos)
					{
						quepos -= volume;
						continue;
					}
					else if (quepos != 0)
					{
						// 如果成交量大于排队位置，部分可成交
						volume -= quepos;
						quepos = 0;
					}
				}
				else if (!ordInfo._positive)
				{
					// 被动成交时使用剩余订单量
					volume = ordInfo._left;
				}

				// 计算实际成交数量
				double qty = min(volume, ordInfo._left);
				if (decimal::eq(qty, 0.0))
					qty = 1;

				// 发送成交回调
				_sink->handle_trade(localid, ordInfo._code, ordInfo._buy, qty, ordInfo._price, price, ordInfo._time);
				
				// 更新订单状态
				ordInfo._traded += qty;
				ordInfo._left -= qty;

				// 发送订单状态更新
				_sink->handle_order(localid, ordInfo._code, ordInfo._buy, ordInfo._left, price, false, ordInfo._time);

				// 如果订单完全成交，加入删除列表
				if (ordInfo._left == 0)
					to_erase.emplace_back(localid);
			}
		}
	}
}

/**
 * @brief 更新限价订单簿
 * @details 根据最新行情数据更新限价订单簿，包括价格档位和委托量信息
 * 
 * @param curTick 当前行情数据
 */
void MatchEngine::update_lob(WTSTickData* curTick)
{
	// 获取当前合约的限价订单簿引用
	LmtOrdBook& curBook = _lmt_ord_books[curTick->code()];
	
	// 更新订单簿的核心价格信息
	curBook._cur_px = PRICE_DOUBLE_TO_INT(curTick->price());    // 最新价
	curBook._ask_px = PRICE_DOUBLE_TO_INT(curTick->askprice(0));// 卖一价
	curBook._bid_px = PRICE_DOUBLE_TO_INT(curTick->bidprice(0));// 买一价

	// 更新十档行情数据
	for (uint32_t i = 0; i < 10; i++)
	{
		// 如果买卖价格都为0，说明没有更多档位数据，跳出循环
		if (PRICE_DOUBLE_TO_INT(curTick->askprice(i)) == 0 && PRICE_DOUBLE_TO_INT(curTick->bidprice(i)) == 0)
			break;

		// 处理卖方报价档位
		uint32_t px = PRICE_DOUBLE_TO_INT(curTick->askprice(i));
		if (px != 0)
		{
			double& volume = curBook._items[px];
			volume = curTick->askqty(i);
		}

		// 处理买方报价档位
		px = PRICE_DOUBLE_TO_INT(curTick->bidprice(i));
		if (px != 0)
		{
			double& volume = curBook._items[px];
			volume = curTick->bidqty(i);  // 修正：应该使用bidqty而不是askqty
		}
	}

	// 清理买一卖一之间的无效报价
	// 在买一价和卖一价之间不应该有任何有效的委托订单
	if (!curBook._items.empty())
	{
		// 找到买一价位置的迭代器
		auto sit = curBook._items.lower_bound(curBook._bid_px);
		if (sit->first == curBook._bid_px)
			sit++;  // 跳过买一价本身

		// 找到卖一价位置的迭代器
		auto eit = curBook._items.lower_bound(curBook._ask_px);

		// 删除买一价和卖一价之间的所有价格档位
		if (sit->first <= eit->first)
			curBook._items.erase(sit, eit);
	}
}

/**
 * @brief 提交买单
 * @details 创建并提交一个买入订单，计算排队位置和主动成交标志
 * 
 * @param stdCode 标准合约代码
 * @param price 限价价格
 * @param qty 委托数量
 * @param curTime 当前时间戳
 * @return OrderIDs 返回订单ID列表
 */
OrderIDs MatchEngine::buy(const char* stdCode, double price, double qty, uint64_t curTime)
{
	// 获取最新行情数据
	WTSTickData* lastTick = grab_last_tick(stdCode);
	if (lastTick == NULL)
		return OrderIDs();

	// 生成本地订单ID并创建订单信息
	uint32_t localid = makeLocalOrderID();
	OrderInfo& ordInfo = _orders[localid];
	strcpy(ordInfo._code, stdCode);
	ordInfo._buy = true;
	ordInfo._limit = price;
	ordInfo._qty = qty;
	ordInfo._left = qty;
	ordInfo._price = lastTick->price();

	// 判断订单类型：如果价格大于等于卖一价，则为主动买单
	// 如果价格等于买一价，则排队等待，排队位置为买一档数量
	if (decimal::ge(price, lastTick->askprice(0)))
		ordInfo._positive = true;
	else if (decimal::eq(price, lastTick->bidprice(0)))
		ordInfo._queue = lastTick->bidqty(0);
	if (decimal::eq(price, lastTick->price()))
		ordInfo._queue = (uint32_t)round((lastTick->askqty(0)*lastTick->askprice(0) + lastTick->bidqty(0)*lastTick->bidprice(0)) / (lastTick->askprice(0) + lastTick->bidprice(0)));

	// 根据撤单率调整排队位置，模拟部分委托被撤单的情况
	ordInfo._queue -= (uint32_t)round(ordInfo._queue*_cancelrate);
	ordInfo._time = curTime;

	lastTick->release();

	OrderIDs ret;
	ret.emplace_back(localid);
	return ret;
}

/**
 * @brief 提交卖单
 * @details 创建并提交一个卖出订单，计算排队位置和主动成交标志
 * 
 * @param stdCode 标准合约代码
 * @param price 限价价格
 * @param qty 委托数量
 * @param curTime 当前时间戳
 * @return OrderIDs 返回订单ID列表
 */
OrderIDs MatchEngine::sell(const char* stdCode, double price, double qty, uint64_t curTime)
{
	// 获取最新行情数据
	WTSTickData* lastTick = grab_last_tick(stdCode);
	if (lastTick == NULL)
		return OrderIDs();

	// 生成本地订单ID并创建订单信息
	uint32_t localid = makeLocalOrderID();
	OrderInfo& ordInfo = _orders[localid];
	strcpy(ordInfo._code, stdCode);
	ordInfo._buy = false;
	ordInfo._limit = price;
	ordInfo._qty = qty;
	ordInfo._left = qty;
	ordInfo._price = lastTick->price();

	// 判断订单类型：如果价格等于卖一价，则排队等待，排队位置为卖一档数量
	// 如果价格小于等于买一价，则为主动卖单
	if (decimal::eq(price, lastTick->askprice(0)))
		ordInfo._queue = lastTick->askqty(0);
	else if (decimal::le(price, lastTick->bidprice(0)))
		ordInfo._positive = true;
	if (decimal::eq(price, lastTick->price()))
		ordInfo._queue = (uint32_t)round((lastTick->askqty(0)*lastTick->askprice(0) + lastTick->bidqty(0)*lastTick->bidprice(0)) / (lastTick->askprice(0) + lastTick->bidprice(0)));

	// 根据撤单率调整排队位置，模拟部分委托被撤单的情况
	ordInfo._queue -= (uint32_t)round(ordInfo._queue*_cancelrate);
	ordInfo._time = curTime;

	lastTick->release();

	OrderIDs ret;
	ret.emplace_back(localid);
	return ret;
}

/**
 * @brief 批量撤单
 * @details 根据合约代码和买卖方向批量撤销订单
 * 
 * @param stdCode 标准合约代码
 * @param isBuy 是否为买单
 * @param qty 撤单数量，0表示撤销所有
 * @param cb 撤单回调函数
 * @return OrderIDs 返回被撤销的订单ID列表
 */
OrderIDs MatchEngine::cancel(const char* stdCode, bool isBuy, double qty, FuncCancelCallback cb)
{
	OrderIDs ret;
	for (auto& v : _orders)
	{
		OrderInfo& ordInfo = (OrderInfo&)v.second;
		// 跳过非活跃状态的订单
		if (ordInfo._state != 1)
			continue;

		double left = qty;
		// 匹配买卖方向
		if (ordInfo._buy == isBuy)
		{
			uint32_t localid = v.first;
			ret.emplace_back(localid);
			ordInfo._state = 9;  // 设置为已撤销状态
			cb(ordInfo._left*(ordInfo._buy ? 1 : -1));

			// 如果指定了撤单数量，需要检查是否达到撤单量
			if (qty != 0)
			{
				if ((int32_t)left <= ordInfo._left)
					break;

				left -= ordInfo._left;
			}
		}
	}

	return ret;
}

/**
 * @brief 撤销指定订单
 * @details 根据本地订单ID撤销特定订单
 * 
 * @param localid 本地订单ID
 * @return double 返回剩余未成交数量（买单为正，卖单为负）
 */
double MatchEngine::cancel(uint32_t localid)
{
	auto it = _orders.find(localid);
	if (it == _orders.end())
		return 0.0;

	OrderInfo& ordInfo = (OrderInfo&)it->second;
	ordInfo._state = 9;  // 设置为已撤销状态

	return ordInfo._left*(ordInfo._buy ? 1 : -1);
}

/**
 * @brief 处理行情数据
 * @details 处理新的行情数据，更新订单簿，触发订单状态检查和撮合
 * 
 * @param stdCode 标准合约代码
 * @param curTick 当前行情数据
 */
void MatchEngine::handle_tick(const char* stdCode, WTSTickData* curTick)
{
	if (NULL == curTick)
		return;

	// 初始化行情缓存
	if (NULL == _tick_cache)
		_tick_cache = WTSTickCache::create();

	// 添加行情数据到缓存
	_tick_cache->add(stdCode, curTick, true);

	// 更新限价订单簿
	update_lob(curTick);

	OrderIDs to_erase;
	// 检查订单状态，处理到期或需要触发的订单
	fire_orders(stdCode, to_erase);

	// 执行订单撮合
	match_orders(curTick, to_erase);

	// 清理已完成的订单
	for (uint32_t localid : to_erase)
	{
		auto it = _orders.find(localid);
		if (it != _orders.end())
			_orders.erase(it);
	}
}

/**
 * @brief 获取最新行情数据
 * @details 从行情缓存中获取指定合约的最新行情数据
 * 
 * @param stdCode 标准合约代码
 * @return WTSTickData* 返回最新行情数据指针，如果没有数据则返回NULL
 */
WTSTickData* MatchEngine::grab_last_tick(const char* stdCode)
{
	if (NULL == _tick_cache)
		return NULL;

	return (WTSTickData*)_tick_cache->grab(stdCode);
}